/*
 * Copyright 2011 Vlad Goldman.  All rights reserved.
  
   This file is part of GooOutSync - Google Outlook Sync application
  
    Project page: http://goooutsync.googlecode.com

    GooOutSync is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    GooOutSync is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with GooOutSync.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.vgold.goooutsync;

import org.apache.log4j.Logger;

import com.google.gdata.data.DateTime;

public abstract class AContact extends GenericContactItem {
	private Logger logger = Logger.getLogger(AContact.class);;
	/**
	 * 
	 */
	private static final long serialVersionUID = 4553542255364762835L;

	public enum Fields {
		TITLE, FIRST, MIDDLE, LAST, NICKNAME, BUSINESS_PHONE, BUSINESS_PHONE2, MOBILE_PHONE, MOBILE_PHONE2, HOME_PHONE, HOME_PHONE2, OTHER_PHONE, EMAIL1, EMAIL2, EMAIL3, COMPANY, BUSINESS_ADDR, HOME_ADDR, BIRTHDAY, NOTES, OUTID, GOOID, LASTMODIFIED, PRIMARY_ADDR_IND
	}

	Boolean pictureChanged = false;
	DateTime modified;

	/**
	 * @return the modified
	 */
	public DateTime getModified() {
		return modified;
	}

	/**
	 * @param modified
	 *            the modified to set
	 */
	public void setModified(DateTime modified) {
		this.modified = modified;
	}

	Boolean changed = false;

	public String getField(Fields field) {
		String temp = this.get(field);
		return temp == null ? "" : temp;
	}

	public void setField(Fields field, String value) {
		// changed = true;
		this.put(field, value);
	}

	public void updateWith(AContact contact) throws GooOutSyncException {
		for (Fields field : Fields.values()) {
			if (!field.equals(Fields.LASTMODIFIED)
					&& !contact.getField(field).equals("")) {
				this.setField(field, contact.getField(field));
				this.changed = true;
				if (this instanceof OutlookContact) {
					logger.debug("Updating Outlook contact field " + field
							+ " with " + contact.getField(field));
					((OutlookContact) this).updateField(field,
							contact.getField(field));
				} else if (this instanceof GoogleContact) {
					logger.debug("Updating Google contact field " + field
							+ " with " + contact.getField(field));
					((GoogleContact) this).updateField(field,
							contact.getField(field));
				} else {
					throw new GooOutSyncException(
							"BUG: contact is not Outlook or Google");
				}
			}
		}
		// this.setPictureBytes(contact.getPictureBytes(refContact.getGooglePictureHash()));
	}

	public abstract void save();

	public abstract void delete();

	public abstract String getPictureHash();

	public abstract byte[] getPictureBytes(String lastEtag);

	public abstract void setPictureBytes(byte[] pictureBytes);

	public String getHashFromBytes(byte[] pictureBytes) {
		if (pictureBytes == null)
			return "";
		int index;
		int fileLength = pictureBytes.length;
		int[] hash = new int[10];
		for (index = 0; index < fileLength; index++) {
			hash[index % 10] += pictureBytes[index];
		}
		char[] hashChars = new char[10];
		for (index = 0; index < 10; index++) {
			hashChars[index] = (char) ('a' + (Math.abs(hash[index]) % 25));
		}
		return new String(hashChars);

	}

	public boolean getDeleted() {
		return false;
	}

	public boolean equals(AContact aContact) {

		if (this == aContact)
			return true;

		for (Fields field : Fields.values()) {
			if (!field.equals(Fields.LASTMODIFIED)) {
				switch (field) {
				case BUSINESS_PHONE:
				case BUSINESS_PHONE2:
				case MOBILE_PHONE:
				case MOBILE_PHONE2:
				case HOME_PHONE:
				case HOME_PHONE2:
				case OTHER_PHONE:
					if (0 != this
							.getField(field)
							.replaceAll("\\.\\-\\(\\)\\s\\,\\+", "")
							.compareTo(
									aContact.getField(field).replaceAll(
											"\\.\\-\\(\\)\\s\\,\\+", ""))) {
						logger.warn("Contact field " + field + " different.");
						logger.warn(field.toString()
								+ this.getField(field).length() + ":"
								+ this.getField(field));
						logger.warn(field.toString()
								+ aContact.getField(field).length() + ":"
								+ aContact.getField(field));
						// logger.debug(Arrays.toString(aContact.getField(field).getBytes()));
						return false;
					}
					break;
				default:
					if (0 != this
							.getField(field)
							.replaceAll("\\p{Cntrl}", "")
							.compareTo(
									aContact.getField(field).replaceAll(
											"\\p{Cntrl}", ""))) {
						logger.warn("Contact field " + field + " different.");
						logger.warn(field.toString()
								+ this.getField(field).length() + ":"
								+ this.getField(field));
						logger.warn(field.toString()
								+ aContact.getField(field).length() + ":"
								+ aContact.getField(field));
						// logger.debug(Arrays.toString(aContact.getField(field).getBytes()));
						return false;
					}
					break;
				}
			}
		}
		return true;
	}

	public void syncWith(AContact aContact) throws GooOutSyncException {

		if (this == aContact)
			return;
		if (this.modified.compareTo(aContact.modified) > 0) {
			logger.info("This contact is the last updated on:" + this.modified);
			logger.info("Ref contact is updated on:" + aContact.modified);
			// this contact is last updated, propagate changes to ref contact
			aContact.updateWith(this);
		} else if (this.modified.compareTo(aContact.modified) > 0) {
			logger.info("This contact is updated on:" + this.modified);
			logger.info("Ref contact is the last updated on:"
					+ aContact.modified);
			// this object is older, propagate changes from ref contact
			this.updateWith(aContact);
		} else {
			logger.info("Update date equal: " + this.modified + " "
					+ aContact.modified);
			// update date is equal? should not happen
			// anyway do nothing or invoke manual conflict resolution
		}
		return;
	}

	public boolean isChanged() {
		return changed;
	}
}
